import * as fs from 'fs';
import * as path from 'path';

export interface ParameterInfo {
  type: string;
  name: string;
  annotation?: string;
}

export interface MethodInfo {
  methodName: string;
  className: string;
  parameters: ParameterInfo[];
  lineNumber: number;
  fullSignature: string;
}

export interface ParameterObjectResult {
  success: boolean;
  message: string;
  parameterObjectClass?: string;
  parameterObjectFile?: string;
  updatedMethods: Array<{
    filePath: string;
    methodName: string;
    oldSignature: string;
    newSignature: string;
  }>;
  refactorDetails?: string[];
  errors?: string[];
}

/**
 * 参数对象重构器 - 将多个参数重构为参数对象
 * 功能：分析方法参数，生成参数对象类，更新方法签名和调用
 */
export class ParameterObjectRefactor {

  async introduceParameterObject(
    filePath: string,
    methodName: string,
    parameterObjectName: string,
    projectPath: string,
    minParameterCount: number = 3
  ): Promise<ParameterObjectResult> {
    const result: ParameterObjectResult = {
      success: false,
      message: '',
      updatedMethods: [],
      refactorDetails: [],
      errors: []
    };

    try {
      console.error(`[ParameterObjectRefactor] 开始参数对象重构`);
      console.error(`[ParameterObjectRefactor] 源文件: ${filePath}`);
      console.error(`[ParameterObjectRefactor] 方法名: ${methodName}`);
      console.error(`[ParameterObjectRefactor] 参数对象名: ${parameterObjectName}`);
      
      if (!fs.existsSync(filePath)) {
        result.errors?.push('源文件不存在');
        result.message = '源文件不存在';
        return result;
      }

      const sourceContent = fs.readFileSync(filePath, 'utf-8');
      const methodInfo = this.extractMethodInfo(sourceContent, methodName);
      
      if (!methodInfo) {
        result.errors?.push(`未找到方法: ${methodName}`);
        result.message = `未找到方法: ${methodName}`;
        return result;
      }

      console.error(`[ParameterObjectRefactor] 找到方法，参数数量: ${methodInfo.parameters.length}`);

      // 检查参数数量是否达到重构阈值
      if (methodInfo.parameters.length < minParameterCount) {
        result.message = `方法参数数量(${methodInfo.parameters.length})少于最小阈值(${minParameterCount})，无需重构`;
        result.success = true;
        return result;
      }

      // 生成参数对象类
      const parameterObjectClass = this.generateParameterObjectClass(
        parameterObjectName,
        methodInfo.parameters,
        this.extractPackageName(sourceContent)
      );

      // 创建参数对象文件
      const sourceDir = path.dirname(filePath);
      const parameterObjectFile = path.join(sourceDir, `${parameterObjectName}.java`);
      
      if (fs.existsSync(parameterObjectFile)) {
        result.errors?.push(`参数对象文件已存在: ${parameterObjectFile}`);
        result.message = '参数对象文件已存在';
        return result;
      }

      fs.writeFileSync(parameterObjectFile, parameterObjectClass, 'utf-8');
      result.parameterObjectFile = parameterObjectFile;
      result.parameterObjectClass = parameterObjectClass;
      result.refactorDetails?.push(`创建参数对象文件: ${parameterObjectFile}`);

      // 更新原方法
      const newSignature = this.generateNewMethodSignature(methodInfo, parameterObjectName);
      const updatedContent = this.updateMethodInFile(sourceContent, methodInfo, newSignature);
      
      fs.writeFileSync(filePath, updatedContent, 'utf-8');
      result.updatedMethods.push({
        filePath,
        methodName: methodInfo.methodName,
        oldSignature: methodInfo.fullSignature,
        newSignature
      });
      result.refactorDetails?.push(`更新方法签名: ${methodInfo.methodName}`);

      // 查找并更新所有调用该方法的地方
      const callers = await this.findMethodCallers(projectPath, methodInfo.className, methodName, filePath);
      console.error(`[ParameterObjectRefactor] 找到 ${callers.length} 个方法调用`);

      for (const caller of callers) {
        try {
          const callerUpdated = await this.updateMethodCalls(
            caller.filePath,
            methodInfo,
            parameterObjectName
          );
          
          if (callerUpdated) {
            result.updatedMethods.push({
              filePath: caller.filePath,
              methodName: caller.callingMethod || 'unknown',
              oldSignature: `调用 ${methodName}`,
              newSignature: `使用参数对象调用 ${methodName}`
            });
            result.refactorDetails?.push(`更新方法调用: ${caller.filePath}`);
          }
        } catch (error) {
          result.errors?.push(`更新调用失败 ${caller.filePath}: ${error}`);
        }
      }

      result.success = true;
      result.message = `成功将 ${methodName} 重构为使用参数对象 ${parameterObjectName}`;

    } catch (error) {
      result.errors?.push(`参数对象重构失败: ${error}`);
      result.message = `参数对象重构失败: ${error}`;
      console.error(`[ParameterObjectRefactor] 错误:`, error);
    }

    return result;
  }

  private extractMethodInfo(content: string, methodName: string): MethodInfo | null {
    // 匹配方法签名的正则表达式 - 支持注解、修饰符、泛型等
    const methodPattern = new RegExp(
      `^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*` +  // 注解（可选）
      `(?:public|private|protected)?\\s*` +    // 访问修饰符（可选）
      `(?:static\\s+)?(?:final\\s+)?` +        // static/final修饰符（可选）
      `(?:<[^>]+>\\s+)?` +                     // 泛型（可选）
      `\\w+(?:<[^>]+>)?\\s+` +                 // 返回类型
      `${methodName}\\s*\\(` +                 // 方法名和开始括号
      `([^)]+)` +                              // 参数列表（捕获组）
      `\\)`,
      'gm'
    );

    const lines = content.split('\n');
    let match;
    
    while ((match = methodPattern.exec(content)) !== null) {
      const fullMatch = match[0];
      const parametersString = match[1];
      
      // 找到方法所在行号
      const lineNumber = content.substring(0, match.index).split('\n').length;
      
      // 解析参数
      const parameters = this.parseParameters(parametersString);
      
      // 提取类名
      const className = this.extractClassName(content);
      
      if (className) {
        return {
          methodName,
          className,
          parameters,
          lineNumber,
          fullSignature: fullMatch.trim()
        };
      }
    }

    return null;
  }

  private parseParameters(parametersString: string): ParameterInfo[] {
    const parameters: ParameterInfo[] = [];
    
    // 处理空参数列表
    if (!parametersString.trim()) {
      return parameters;
    }

    // 分割参数，考虑泛型中的逗号
    const paramParts = this.splitParameters(parametersString);
    
    for (const part of paramParts) {
      const trimmed = part.trim();
      if (!trimmed) continue;

      // 提取注解
      let annotation: string | undefined;
      let paramPart = trimmed;
      
      const annotationMatch = trimmed.match(/^(@\w+(?:\([^)]*\))?)\s+(.+)$/);
      if (annotationMatch) {
        annotation = annotationMatch[1];
        paramPart = annotationMatch[2];
      }

      // 解析类型和名称
      const parts = paramPart.split(/\s+/);
      if (parts.length >= 2) {
        const name = parts[parts.length - 1];
        const type = parts.slice(0, -1).join(' ');
        
        parameters.push({
          type: type.trim(),
          name: name.trim(),
          annotation
        });
      }
    }

    return parameters;
  }

  private splitParameters(parametersString: string): string[] {
    const params: string[] = [];
    let current = '';
    let depth = 0;
    let inString = false;
    let stringChar = '';

    for (let i = 0; i < parametersString.length; i++) {
      const char = parametersString[i];
      
      if (!inString) {
        if (char === '"' || char === "'") {
          inString = true;
          stringChar = char;
        } else if (char === '<' || char === '(') {
          depth++;
        } else if (char === '>' || char === ')') {
          depth--;
        } else if (char === ',' && depth === 0) {
          params.push(current.trim());
          current = '';
          continue;
        }
      } else if (char === stringChar) {
        inString = false;
      }

      current += char;
    }

    if (current.trim()) {
      params.push(current.trim());
    }

    return params;
  }

  private generateParameterObjectClass(
    className: string,
    parameters: ParameterInfo[],
    packageName: string
  ): string {
    let classContent = '';
    
    // 包声明
    if (packageName) {
      classContent += `package ${packageName};\n\n`;
    }

    // 导入
    const imports = new Set<string>();
    for (const param of parameters) {
      if (param.annotation) {
        if (param.annotation.startsWith('@Valid') || param.annotation.startsWith('@NotNull')) {
          imports.add('import javax.validation.constraints.*;');
        }
      }
    }
    
    if (imports.size > 0) {
      classContent += Array.from(imports).join('\n') + '\n\n';
    }

    // 类声明
    classContent += `/**\n`;
    classContent += ` * 参数对象 - 自动生成\n`;
    classContent += ` */\n`;
    classContent += `public class ${className} {\n\n`;

    // 字段
    for (const param of parameters) {
      if (param.annotation) {
        classContent += `    ${param.annotation}\n`;
      }
      classContent += `    private ${param.type} ${param.name};\n\n`;
    }

    // 无参构造函数
    classContent += `    public ${className}() {}\n\n`;

    // 全参构造函数
    classContent += `    public ${className}(`;
    const constructorParams = parameters.map(p => `${p.type} ${p.name}`).join(', ');
    classContent += constructorParams + ') {\n';
    for (const param of parameters) {
      classContent += `        this.${param.name} = ${param.name};\n`;
    }
    classContent += `    }\n\n`;

    // Getter和Setter
    for (const param of parameters) {
      const capitalizedName = param.name.charAt(0).toUpperCase() + param.name.slice(1);
      
      // Getter
      classContent += `    public ${param.type} get${capitalizedName}() {\n`;
      classContent += `        return ${param.name};\n`;
      classContent += `    }\n\n`;
      
      // Setter
      classContent += `    public void set${capitalizedName}(${param.type} ${param.name}) {\n`;
      classContent += `        this.${param.name} = ${param.name};\n`;
      classContent += `    }\n\n`;
    }

    classContent += '}\n';
    return classContent;
  }

  private generateNewMethodSignature(methodInfo: MethodInfo, parameterObjectName: string): string {
    // 保留原方法的修饰符和返回类型，只改变参数
    const signature = methodInfo.fullSignature;
    const methodNameIndex = signature.indexOf(methodInfo.methodName);
    const beforeMethod = signature.substring(0, methodNameIndex + methodInfo.methodName.length);
    const afterParams = signature.substring(signature.indexOf(')'));
    
    return `${beforeMethod}(${parameterObjectName} request)${afterParams}`;
  }

  private updateMethodInFile(content: string, methodInfo: MethodInfo, newSignature: string): string {
    // 替换方法签名 - 使用更精确的替换逻辑
    let updatedContent = content;
    
    // 找到方法签名的开始和结束位置
    const methodStart = updatedContent.indexOf(methodInfo.fullSignature);
    if (methodStart !== -1) {
      const methodEnd = methodStart + methodInfo.fullSignature.length;
      updatedContent = updatedContent.substring(0, methodStart) + 
                      newSignature + 
                      updatedContent.substring(methodEnd);
    }
    
    // 更新方法体中的参数引用
    const methodBodyPattern = new RegExp(
      `(${newSignature.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\{[^}]*?)([^}]*)(\\})`,
      's'
    );
    
    updatedContent = updatedContent.replace(methodBodyPattern, (match, start, body, end) => {
      let updatedBody = body;
      
      // 替换参数引用为 request.getXxx()
      for (const param of methodInfo.parameters) {
        const capitalizedName = param.name.charAt(0).toUpperCase() + param.name.slice(1);
        const paramPattern = new RegExp(`\\b${param.name}\\b`, 'g');
        updatedBody = updatedBody.replace(paramPattern, `request.get${capitalizedName}()`);
      }
      
      return start + updatedBody + end;
    });
    
    return updatedContent;
  }

  private async findMethodCallers(
    projectPath: string,
    className: string,
    methodName: string,
    excludeFilePath: string
  ): Promise<Array<{ filePath: string; callingMethod?: string; lineNumber: number }>> {
    const callers: Array<{ filePath: string; callingMethod?: string; lineNumber: number }> = [];
    
    const searchDir = async (dirPath: string) => {
      if (!fs.existsSync(dirPath)) return;
      
      const entries = fs.readdirSync(dirPath, { withFileTypes: true });
      
      for (const entry of entries) {
        const fullPath = path.join(dirPath, entry.name);
        
        if (entry.isDirectory()) {
          if (!this.shouldSkipDirectory(entry.name)) {
            await searchDir(fullPath);
          }
        } else if (entry.isFile() && entry.name.endsWith('.java') && fullPath !== excludeFilePath) {
          const fileCallers = await this.findMethodCallersInFile(fullPath, className, methodName);
          callers.push(...fileCallers);
        }
      }
    };
    
    await searchDir(projectPath);
    return callers;
  }

  private async findMethodCallersInFile(
    filePath: string,
    className: string,
    methodName: string
  ): Promise<Array<{ filePath: string; callingMethod?: string; lineNumber: number }>> {
    const callers: Array<{ filePath: string; callingMethod?: string; lineNumber: number }> = [];
    
    try {
      const content = fs.readFileSync(filePath, 'utf-8');
      const lines = content.split('\n');
      
      // 检查是否导入了该类
      const hasImport = content.includes(`import`) && content.includes(className);
      const isInSamePackage = this.extractPackageName(content) === this.getPackageFromFilePath(filePath, className);
      
      if (!hasImport && !isInSamePackage) {
        return callers;
      }
      
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        
        // 查找方法调用 - 各种调用模式
        const callPatterns = [
          new RegExp(`\\b${methodName}\\s*\\(`),              // 直接调用
          new RegExp(`\\.${methodName}\\s*\\(`),               // 对象方法调用
          new RegExp(`${className}\\.${methodName}\\s*\\(`),   // 静态方法调用
        ];
        
        for (const pattern of callPatterns) {
          if (pattern.test(line)) {
            callers.push({
              filePath,
              lineNumber: i + 1
            });
            break;
          }
        }
      }
    } catch (error) {
      console.error(`[ParameterObjectRefactor] 读取文件失败: ${filePath}`, error);
    }
    
    return callers;
  }

  private async updateMethodCalls(
    filePath: string,
    methodInfo: MethodInfo,
    parameterObjectName: string
  ): Promise<boolean> {
    try {
      let content = fs.readFileSync(filePath, 'utf-8');
      const originalContent = content;
      
      // 查找所有方法调用并替换
      const callPattern = new RegExp(
        `(\\w+\\.)?${methodInfo.methodName}\\s*\\(([^)]*)\\)`,
        'g'
      );
      
      content = content.replace(callPattern, (match, objectRef, args) => {
        if (!args.trim()) return match;
        
        // 生成参数对象创建代码
        const paramNames = this.extractCallArguments(args);
        if (paramNames.length !== methodInfo.parameters.length) {
          return match; // 参数数量不匹配，可能是重载方法
        }
        
        let replacement = `${objectRef || ''}${methodInfo.methodName}(new ${parameterObjectName}(${args}))`;
        return replacement;
      });
      
      // 如果有变更，写回文件
      if (content !== originalContent) {
        fs.writeFileSync(filePath, content, 'utf-8');
        console.error(`[ParameterObjectRefactor] 更新方法调用: ${filePath}`);
        return true;
      }
      
    } catch (error) {
      console.error(`[ParameterObjectRefactor] 更新方法调用失败: ${filePath}`, error);
    }
    
    return false;
  }

  private extractCallArguments(argsString: string): string[] {
    return this.splitParameters(argsString);
  }

  private shouldSkipDirectory(dirName: string): boolean {
    const skipDirs = [
      'node_modules', '.git', '.idea', '.vscode', 'target', 'build', 
      'dist', 'out', '.gradle', 'bin', 'logs', 'temp', 'tmp'
    ];
    return skipDirs.includes(dirName) || dirName.startsWith('.');
  }

  private extractClassName(content: string): string | null {
    const match = content.match(/(?:public\s+)?(?:class|interface|enum)\s+(\w+)/);
    return match ? match[1] : null;
  }

  private extractPackageName(content: string): string {
    const match = content.match(/package\s+([^;]+);/);
    return match ? match[1].trim() : '';
  }

  private getPackageFromFilePath(filePath: string, className: string): string {
    // 简单实现：从文件路径推导包名
    const normalizedPath = filePath.replace(/\\/g, '/');
    const javaIndex = normalizedPath.lastIndexOf('/java/');
    if (javaIndex !== -1) {
      const packagePath = normalizedPath.substring(javaIndex + 6, normalizedPath.lastIndexOf('/'));
      return packagePath.replace(/\//g, '.');
    }
    return '';
  }
}